package cloud.tianai.neuron.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * @Author: 天爱有情
 * @date 2022/12/11 10:58
 * @Description 矩阵对象
 */
public class Matrix implements Iterable<List<Double>>, Serializable {

    private List<List<Double>> data;

    public Matrix(int x, int y) {
        this(x, y, 0);
    }

    public Matrix(int x, int y, double initData) {
        data = new ArrayList<>(x);
        for (int i = 0; i < x; i++) {
            List<Double> lists = new ArrayList<>(y);
            for (int a = 0; a < y; a++) {
                lists.add(initData);
            }
            data.add(lists);
        }
    }

    public Matrix(List<List<Double>> data) {
        this.data = data;
    }

    public Matrix(double[][] dataArr) {
        List<List<Double>> dataList = new ArrayList<>(dataArr.length);
        for (double[] doubles : dataArr) {
            ArrayList<Double> list = new ArrayList<>(doubles.length);
            for (double aDouble : doubles) {
                list.add(aDouble);
            }
            dataList.add(list);
        }
        this.data = dataList;
    }

    public void add(List<Double> datas) {
        if (this.data == null) {
            this.data = new ArrayList<>();
        } else if (this.data.size() != 0 && datas.size() != getY()) {
            throw new RuntimeException("列必须一致");
        }
        this.data.add(datas);
    }

    public void set(int x, int y, Double d) {
        d = NumPy.intercept(d);
        data.get(x).set(y, d);
    }

    public Double get(int x, int y) {
        return data.get(x).get(y);
    }

    public List<Double> get(int x) {
        return data.get(x);
    }

    public Matrix getToMatrix(int x) {
        List<Double> doubles = data.get(x);
        Matrix matrix = new Matrix(0, 0);
        matrix.add(doubles);
        return matrix;
    }

    public Matrix getToMatrix(int x, int y) {
        Double aDouble = data.get(x).get(y);
        Matrix matrix = new Matrix(1, 1);
        matrix.set(0, 0, aDouble);
        return matrix;
    }

    public List<List<Double>> getData() {
        return this.data;
    }
    public double[][] getBasicData() {
       return NumPy.ListArray2D(data);
    }

    public int getX() {
        return size();
    }

    public int getY() {
        if (data != null && data.size() > 0) {
            return data.get(0).size();
        }
        return 0;
    }

    public int size() {
        if (data != null) {
            return data.size();
        }
        return 0;
    }

    public Matrix T() {
        Matrix result = new Matrix(getY(), getX());
        List<List<Double>> data = getData();
        for (int x = 0; x < data.size(); x++) {
            List<Double> rows = data.get(x);
            for (int y = 0; y < rows.size(); y++) {
                result.set(y, x, rows.get(y));
            }
        }
        return result;
    }

    public Matrix dot(Matrix b) {
        return NumPy.dot(this, b);
    }

    public Matrix copy() {
        return NumPy.copy(this);
    }

    public Matrix sin() {
        return NumPy.sin(this);
    }

    public Matrix divide(double reduceData) {
        return NumPy.divide(this, reduceData, true);
    }

    public Matrix divide(List<Double> yArr) {
        return NumPy.divide(this, yArr, true);
    }

    public Matrix divide(Matrix b) {
        return NumPy.divide(this, b);
    }

    public Matrix divide(Matrix b, boolean ignoreZero) {
        return NumPy.divide(this, b, ignoreZero);
    }


    public Matrix sub(Matrix b) {
        return NumPy.sub(this, b);
    }

    public Matrix plus(Matrix b) {
        return NumPy.plus(this, b);
    }

    public Matrix sub(List<Double> reduceDatas) {
        return NumPy.sub(this, reduceDatas);
    }

    public Matrix multiply(Matrix b) {
        return NumPy.multiply(this, b);
    }

    public Matrix multiply(double b) {
        return NumPy.multiply(this, b);
    }

    public Matrix pow(int i) {
        return NumPy.pow(this, i);
    }

    public Matrix mean(int i) {
        return NumPy.mean(this, i);
    }

    public Matrix std(int i) {
        return NumPy.std(this, i);
    }

    @Override
    public Iterator<List<Double>> iterator() {
        return data.iterator();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[Matrix]").append("\n");
        sb.append("(").append(getX()).append(",").append(getY()).append(")").append("\n");
        for (List<Double> datum : data) {
            sb.append(datum).append("\n");
        }
        return sb.toString();
    }

    public void set(int i1, List<Double> doubles) {
        if (doubles.size() != getY()) {
            throw new RuntimeException("列必须一致");
        }
        data.set(i1, doubles);
    }

    public Matrix min(int i) {
        return NumPy.min(this, i);
    }

    public Matrix minMax(int i) {
        return NumPy.minMax(this, i);
    }

    public Matrix max(int i) {
        return NumPy.max(this, i);
    }
}
